package com.kate.mall.bff.wx.service;

import cn.hutool.core.map.MapUtil;
import com.kate.mall.bff.wx.feign.CouponFeignClient;
import com.kate.mall.bff.wx.feign.ProductFeignClient;
import com.kate.mall.bff.wx.feign.SnmFeignClient;
import com.kate.mall.bff.wx.vo.HomeChannelVo;
import com.kate.mall.bff.wx.vo.HomeFloorGoodsListVo;
import com.kate.mall.bff.wx.vo.goods.FloorGoodsVo;
import com.kate.mall.bff.wx.vo.goods.NewOrHotGoodsVo;
import com.kate.mall.bff.wx.vo.coupon.CouponVo;
import com.kate.mall.common.vo.messsage.MessageRowsVo;
import com.kate.mall.common.exception.NDException;
import com.kate.mall.common.utils.ListMap2ListVoUtils2;
import com.kate.mall.common.utils.R;
import com.kate.mall.common.utils.ThreadPoolUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author 新梦想.陈超
 * @version 2021.2
 * @Description: {TODO}
 * @date 2022/12/16 下午2:11
 *

 *
 *
 *
 *
 *
 */

@Service
public class HomeService2 {

    @Resource
    private ProductFeignClient productFeignClient;

    @Resource
    private ListMap2ListVoUtils2 listMap2ListVoUtils;
    @Resource
    private CouponFeignClient couponFeignClient;

    @Resource
    private SnmFeignClient snmFeignClient;
    @Resource
    private GrouponService grouponService;
    /**
     * @Cacheable  作用就是将方法的返回值，以指定key的方式保存redis中，以后再调用该方法，先从redis中查询数据
     *             如果有，则直接返回，如果没有，则执行当前方法，远程调用，进行数据库的查询
     *cacheNames   缓存名
     *key：        缓存键
     *unless       指定方法返回值满足条件，则不缓存
     * #result['data']?.size()  就是方法返回值的data为key,对应的value的size为0 ，则不缓存返回值
     *
     *
     * 1.如何防止突刺流量访问？
     *  首页缓存预热：上线前或上线时，将首页数据查询出来，进行缓存！！
     *          系统启动任务提：CommandLineRunner 和 ApplicationRunner,
     * 2.如果缓存失效，那一刹那，突然高并发访问怎么办？
     *      1)上线时间：晚上或凌晨2点
     *      2）缓存失效起始时间也是从凌晨2点开始，失效时间以天单位。
     * 3.首页缓存与数据库数据的一致性如何处理？
     *     1）设置首页缓存时间3天/一周
     *     2）广告、新品、热品的更新必须提前3天/一周
     *
     * 4.缓存key的设计
     * cacheNames:服务名
     * key:业务:表:唯一标示
     * @return
     */
    @Cacheable(cacheNames = "wx-bff",key = "'h:i'",unless="#result['data']?.size()==0")
    public R list() {
        Map resultMap = new HashMap();
        //TODO 1.新品列表   page=1  limit=6
        ThreadPoolExecutor pool = ThreadPoolUtils.instance(true);
        try {
            //解决：------------------------------------------------------------------------------------------------------------
            //获取旧线程的RequestAttributes
            //RequestContextHolder底层使用的是ThreadLocal
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            //解决：----------------------------------------------------------------------------------------------------------

            CompletableFuture<R> newGoodsFuture= CompletableFuture.supplyAsync(()->{
                //解决：-----------------------------------------------------------------------------------------------------
                //将旧线程中的RequestAttributes，设置到线程中的RequestContextHolder里
                RequestContextHolder.setRequestAttributes(requestAttributes);
                //解决：-----------------------------------------------------------------------------------------------------
                return productFeignClient.queryNewOrHotGoodsList(generatorParams(
                        new String[]{"limit", "isNew", "sort", "order"},
                        new Object[]{"6", 1, "sort_order", "asc"}));
            },pool);
            //TODO 2.优惠券列表 page=1  limit=4
            CompletableFuture<R> couponListFuture= CompletableFuture.supplyAsync(()->{
                //解决：-----------------------------------------------------------------------------------------------------
                //将旧线程中的RequestAttributes，设置到线程中的RequestContextHolder里
                RequestContextHolder.setRequestAttributes(requestAttributes);
                //解决：-----------------------------------------------------------------------------------------------------

                return  couponFeignClient.selectCouponList(generatorParams(
                        new String[]{"limit", "sort","order"},
                        new Object[]{"4", "add_time","asc"}));
            },pool);
            //TODO 3.分类列表 page=1  limit=10
            CompletableFuture<R> channelListFuture= CompletableFuture.supplyAsync(()->{
                //解决：-----------------------------------------------------------------------------------------------------
                //将旧线程中的RequestAttributes，设置到线程中的RequestContextHolder里
                RequestContextHolder.setRequestAttributes(requestAttributes);
                //解决：-----------------------------------------------------------------------------------------------------
                return productFeignClient.queryCategoryList(generatorParams(new String[]{"limit"},
                        new Object[]{"10"}));
            },pool);
            //TODO 4.团购专区列表 page=1  limit=3
            CompletableFuture<R> groupOnListFuture= CompletableFuture.supplyAsync(()->{
                //解决：-----------------------------------------------------------------------------------------------------
                //将旧线程中的RequestAttributes，设置到线程中的RequestContextHolder里
                RequestContextHolder.setRequestAttributes(requestAttributes);
                //解决：-----------------------------------------------------------------------------------------------------
                return grouponService.grouponRules(generatorParams(new String[]{"limit"},
                        new Object[]{"3"}));
            },pool);
            //TODO 5.轮播图列表  page=1  limit=5
            //TODO 6.品牌专区   page=1  limit=4
            CompletableFuture<R> brandListFuture= CompletableFuture.supplyAsync(()->{
                //解决：-----------------------------------------------------------------------------------------------------
                //将旧线程中的RequestAttributes，设置到线程中的RequestContextHolder里
                RequestContextHolder.setRequestAttributes(requestAttributes);
                //解决：-----------------------------------------------------------------------------------------------------
                return productFeignClient.queryBrandList(generatorParams(new String[]{"limit", "sort", "order"},
                        new Object[]{"4", "add_time", "desc"}));
            },pool);
            //TODO 7.热卖商品列表 page=1  limit=6
            CompletableFuture<R> hotGoodsFuture= CompletableFuture.supplyAsync(()->{
                //解决：-----------------------------------------------------------------------------------------------------
                //将旧线程中的RequestAttributes，设置到线程中的RequestContextHolder里
                RequestContextHolder.setRequestAttributes(requestAttributes);
                //解决：-----------------------------------------------------------------------------------------------------
                return productFeignClient.queryNewOrHotGoodsList(generatorParams(new String[]{"limit", "isHot", "sort", "order"},
                        new Object[]{"6", 1, "sort_order", "asc"}));
            },pool);

            //TODO 8.专题列表   page=1  limit=4
            //TODO 9.楼层商品列表 page=1  limit=4
            CompletableFuture<R> floorGoodsListFuture= CompletableFuture.supplyAsync(()->{
                //解决：-----------------------------------------------------------------------------------------------------
                //将旧线程中的RequestAttributes，设置到线程中的RequestContextHolder里
                RequestContextHolder.setRequestAttributes(requestAttributes);
                //解决：-----------------------------------------------------------------------------------------------------

                return productFeignClient.queryFloorGoodsList(1, 4);
            },pool);
            //TODO 10.模板消息
            CompletableFuture<R> messageFuture= CompletableFuture.supplyAsync(()->{
                //解决：-----------------------------------------------------------------------------------------------------
                //将旧线程中的RequestAttributes，设置到线程中的RequestContextHolder里
                RequestContextHolder.setRequestAttributes(requestAttributes);
                //解决：-----------------------------------------------------------------------------------------------------

                return snmFeignClient.refreshMessage();
            },pool);
            parseFuture(newGoodsFuture, resultMap, "newGoodsList", NewOrHotGoodsVo.class);
            parseFuture(hotGoodsFuture, resultMap, "hotGoodsList", NewOrHotGoodsVo.class);
            parseFutureMsg(messageFuture, resultMap, "messageRows", MessageRowsVo.class);
            parseFuture(couponListFuture,resultMap, "couponList", CouponVo.class);
            parseFuture(channelListFuture,resultMap,"channel", HomeChannelVo.class);
            this.parseFutureFloorGoodsList(floorGoodsListFuture,resultMap,"floorGoodsList", HomeFloorGoodsListVo.class);
        } finally {
            pool.shutdown();
        }
        return R.ok().put("data", resultMap);
    }
    public void  parseFutureFloorGoodsList(CompletableFuture<R> future, Map resultMap, String responseKey, Class voClazz){
        R listR = null;
        try {
            listR = future.get();
        } catch (Exception e) {
            throw new NDException("首页接口请求数据异常....");
        }
        if(listR==null) return;
        List<Map> mapList = MapUtil.get(MapUtil.get(listR, "page", Map.class), "list", List.class);
        List<HomeFloorGoodsListVo> voList = listMap2ListVoUtils.listMap2ListVo(mapList, voClazz);
        for (HomeFloorGoodsListVo homeFloorGoodsListVo : voList) {
            for (Map floor : mapList) {
                List<Map> floorGoodsList = (List<Map>) floor.get("goodsList");
                List<FloorGoodsVo> floorGoodsVoList = listMap2ListVoUtils.listMap2ListVo(floorGoodsList, FloorGoodsVo.class);
                homeFloorGoodsListVo.setGoodsList(floorGoodsVoList);
            }
        }
        resultMap.put(responseKey, voList);
    }

    public void parseFutureMsg(CompletableFuture<R> messageFuture, Map resultMap, String responseKey, Class<MessageRowsVo> voClazz) {
        R listR = null;
        try {
            listR = messageFuture.get();
        } catch (Exception e) {
            throw new NDException("首页消息请求数据异常....");
        }
        if(listR==null) return;
        MessageRowsVo messageRowsVo = null;
        try {
            messageRowsVo = voClazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        messageRowsVo.setLastRows((Integer) listR.get("lastRows"));
        messageRowsVo.setUnreadRows((Integer) listR.get("unreadRows"));
        resultMap.put(responseKey, messageRowsVo);
    }

    private void parseFuture(CompletableFuture<R> future, Map resultMap, String responseKey, Class voClazz)  {
        R listR = null;
        try {
            listR = future.get();
        } catch (Exception e) {
            throw new NDException("首页接口请求数据异常....");
        }
        if(listR==null) return;
        List<Map> mapList = MapUtil.get(MapUtil.get(listR, "page", Map.class), "list", List.class);
        //裁剪字段两种方案：一、自己写工具类(框架)  二 、mapStruct框架
        //List<map>====>list<vo>
        List VoList = listMap2ListVoUtils.listMap2ListVo(mapList, voClazz);
        resultMap.put(responseKey, VoList);
    }

    public Map<String, Object> generatorParams(String[] keys, Object[] values) {
        ConcurrentHashMap<String, Object> params = MapUtil.newConcurrentHashMap();
        if (keys.length != values.length) {
            throw new RuntimeException("键值数量不匹配，参数错误");
        }
        for (int i = 0; i < keys.length; i++) {
            params.put(keys[i], values[i]);
        }
        return params;
    }
}
